package pf

import (
	"fmt"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/json"
	"go.manyji.com/lib/net/socks5"
	"io"
	"net/http"
	"sync"
)

var (
	fProxyServer = config.String("proxy.server", "", "")
	fTryCnt      = config.Int("proxy.trycnt", 3, "")

	pools = map[string]*ProxyPool{}
	pl    = sync.Mutex{}
)

type ProxyPool struct {
	country string
	pool    sync.Pool
}

func GetProxyPool(country string) *ProxyPool {
	pl.Lock()
	defer pl.Unlock()
	if p, ok := pools[country]; ok {
		return p
	}
	p := &ProxyPool{
		country: country,
		pool: sync.Pool{
			New: func() any {
				p, err := GetProxy(country)
				if err != nil {
					return nil
				}
				return p
			},
		},
	}
	pools[country] = p
	return p
}

func (p *ProxyPool) Get() *socks5.Proxy {
	v := p.pool.Get()
	if v == nil {
		return nil
	}
	return v.(*socks5.Proxy)
}

func (p *ProxyPool) Put(proxy *socks5.Proxy) {
	p.pool.Put(proxy)
}

func GetProxy(country string) (*socks5.Proxy, error) {
	for i := 0; i < *fTryCnt; i++ {
		p, err := getProxy(country)
		if err != nil {
			if err.Error() == "not found" {
				continue
			}
			return nil, err
		}
		return p, nil
	}
	return nil, fmt.Errorf("not found")
}

func getProxy(country string) (*socks5.Proxy, error) {
	args := "pf=" + pfname
	if country != "" {
		args += "&country=" + country
	}
	resp, err := http.Get("http://" + *fProxyServer + "/getproxy?" + args)
	if err != nil {
		return nil, err
	}
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		if resp.StatusCode == http.StatusInternalServerError {
			return nil, fmt.Errorf(string(b))
		}
		return nil, fmt.Errorf("http status code: %d", resp.StatusCode)
	}

	node := &proxyNode{}
	if err := json.Unmarshal(b, &node); err != nil {
		return nil, err
	}

	return socks5.New(fmt.Sprintf("%s:%d", node.Host, node.Port), nil), nil
}

type proxyNode struct {
	Host    string `json:"ip"`
	Port    int    `json:"port"`
	IP      string `json:"tip"`
	Country string `json:"country"`
}
